package com.bix.util.blizfiles.dds;

import java.nio.ByteBuffer;

/**
 * This object represents a DDS color block that does not contain alpha
 * information.
 * 
 *	@author		squid
 *
 *	@version	1.0.0
 */
class DDSColorBlock {
	//
	// These are the bit masks for the 4x4 2-bit lookup table.
	//
	// 			00000011, 00001100, 00110000, 11000000
	//
	protected	static	int	masks[] = { 0x03, 0x0C, 0x30, 0xC0};
	
	//
	// These are the shift amounts for each of the 2-bit lookup table entries.
	//
	protected	static	int	shift[] = { 0, 2, 4, 6 };
	
	//
	// There are four color entries per block.
	//
	private	DDSColor[]	colors = new DDSColor [4];
	
	//
	// This is the 4x4 2-bit lookup table for the colors.
	//
	private byte[] 			lookup = new byte[4];

	public DDSColorBlock () {
	}
	
	/**
	 * Instantiate a DDSColorBlock from a byte buffer.
	 * 
	 * @param bb	The buffer to read the block from.
	 */
	public DDSColorBlock (ByteBuffer bb) {
		this.read (bb);
	}
	
	/**
	 * Read the DDSColorBlock from a ByteBuffer.
	 * 
	 * @param bb
	 */
	public void read (ByteBuffer bb) {
		//
		// Read the first two color values (in RGB:565 format).
		//
		this.colors[0] = new DDSColor (((int)bb.getShort ()) & 0xFFFF);
		this.colors[1] = new DDSColor (((int)bb.getShort ()) & 0xFFFF);
		
		//
		// These two colors will be interpolated from the first two later on.
		//
		this.colors[2] = new DDSColor ();
		this.colors[3] = new DDSColor ();
		
		//
		// Read the 4x4 2-bit lookup table (4 x 4 x 2 = 32 bits = 4 bytes).
		//
		bb.get(lookup);

		//
		// Which algorithm do we use to calculate the 3rd and 4th colors?
		//
		long	word;
		if (this.useAlgorithm1()) {
			//
			// Derive the other two colors.
			//
			//	color[2] = 2/3 color[0] + 1/3 color[1]
			//	color[3] = 1/3 color[0] + 2/3 color[1]
			//
			word = ((this.getColors()[0].getRed() * 2 + this.getColors()[1].getRed() + 1) / 3);
			colors[2].setRed((int)(word & 0xFF));
			
			word = (short) ((this.getColors()[0].getGreen() * 2 + this.getColors()[1].getGreen() + 1) / 3);
			colors[2].setGreen((int)(word & 0xFF));
			
			word = (short) ((this.getColors()[0].getBlue() * 2 + this.getColors()[1].getBlue() + 1) / 3);
			colors[2].setBlue ((int)(word & 0xFF));

			word = (short) ((this.getColors()[0].getRed() + this.getColors()[1].getRed() * 2 + 1) / 3);
			colors[3].setRed((int)(word & 0xFF));
			
			word = (short) ((this.getColors()[0].getGreen() + this.getColors()[1].getGreen() * 2 + 1) / 3);
			colors[3].setGreen ((int)(word & 0xFF));
			
			word = (short) ((this.getColors()[0].getBlue() + this.getColors()[1].getBlue() * 2 + 1) / 3);
			colors[3].setBlue((int)(word & 0xFF));
		} else {
			//
			// Since C0 <= C1, then:
			//
			//	color[2] = 1/2 color[0] + 1/2 color[1]
			//	color[3] = Some random number to indicate alpha.
			//
			word = ((this.getColors()[0].getRed() + this.getColors()[1].getRed()) / 2);
			colors[2].setRed((int)(word & 0xFF));
			
			word = (short) ((this.getColors()[0].getGreen() + this.getColors()[1].getGreen()) / 2);
			colors[2].setGreen ((int)(word & 0xFF));
			
			word = (short) ((this.getColors()[0].getBlue() + this.getColors()[1].getBlue()) / 2);
			colors[2].setBlue((int)(word & 0xFF));

			/* random color to indicate alpha */
			colors[3] = new DDSColor (0xFFFF);
		}		
	}
	
	/**
	 * This method determines which algorithm to use for calculating the third
	 * and fourth colors in a color block.
	 * 
	 * @return true if color0 > color 1, false otherwise.
	 */
	protected boolean useAlgorithm1 () {
		return this.getColors()[0].getAs565() > this.getColors ()[1].getAs565();
	}
	
	/**
	 * Decode a row of data.  This decodes a "block" of data.  Each block is
	 * 4 pixels wide by 4 pixels tall.  Each pixel is 32 bits of data (an int)
	 * that contains the red, green, and blue values with no alpha.
	 * 
	 * @param destination
	 * @param offset
	 * @param width
	 * @param colors
	 */
	public void decode (int destination[], int offset, int width) {
		//
		// This is the array of the only four colors that can exist in this block.
		//
		DDSColor[]	colors = this.getColors();
		
		//
		// Step through the four lines of data to be decoded.  The offset is 
		// incremented by width * 3 because each line contains three values per
		// pixel (red, green, and blue).
		//
		for (int y = 0; y < 4; y++, offset += width * 3) {
			//
			// Get the pixel lookup table for this row of four pixels.  Each pixel
			// in the lookup table is two bits and points to one of the four possible
			// colors.
			//
			int	lookup = this.getLookup ()[y];

			//
			// Decode the row.
			//
			for (int x = 0; x < 4; x++ ) {
				//
				// The bits are either 00, 01, 10, or 11 and represent which color to
				// use for the pixel.
				//
				DDSColor	color = colors[(lookup & masks[x]) >> shift[x]];
				destination[offset + x * 3 + 0] = color.getRed();
				destination[offset + x * 3 + 1] = color.getGreen();
				destination[offset + x * 3 + 2] = color.getBlue();
			}
		}
	}
	
	/**
	 * @return the colors
	 */
	public DDSColor[] getColors () {
		return colors;
	}

	/**
	 * @param colors
	 *          the colors to set
	 */
	public void setColors (DDSColor[] colors) {
		this.colors = colors;
	}

	/**
	 * @return the row
	 */
	public byte[] getLookup () {
		return this.lookup;
	}

	/**
	 * @param row
	 *          the row to set
	 */
	public void setLookup (byte[] lookup) {
		this.lookup = lookup;
	}
}
